/** @file    PropertiesProvider.cpp
 *  @author  Alessandro Polo
 *  @version $Id: PropertiesProvider.cpp 2833 2010-08-02 23:12:57Z alex $
 *  @brief
 * File containing methods for the wosh::PropertiesProvider class.
 * The header for this class can be found in PropertiesProvider.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/PropertiesProvider.h>
 #include <core/ObjectAllocator.h>
 #include <core/DataModels.h>
 #include <core/Object.h>
 #include <core/List.h>
 #include <core/Table.h>

#ifdef _ENABLE_PROFILING
 #include <core/SystemProfiler.h>
#endif


using namespace std;
namespace wosh {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DataFieldCollector<PropertyHolder>* PropertyHolder::getDataFields() {
	DataFieldCollector<PropertyHolder>* fields = new DataFieldCollector<PropertyHolder>();
	DataFieldSubWrapperT<PropertyHolder, const Property*, const std::string&, Variant>* dfwPropertyName;
	dfwPropertyName = new DataFieldSubWrapperT<PropertyHolder, const Property*, const std::string&, Variant>(&PropertyHolder::getProperty, &Property::getKey, "Name", 0);
	fields->add(dfwPropertyName);
	DataFieldSubWrapperT<PropertyHolder, const Property*, const Variant&, Variant>* dfwPropertyValue;
	dfwPropertyValue = new DataFieldSubWrapperT<PropertyHolder, const Property*, const Variant&, Variant>(&PropertyHolder::getProperty, &Property::getValue, "Value", 1);
	fields->add(dfwPropertyValue);
	DataFieldSubWrapperT<PropertyHolder, const Property*, const Permission&,Permission>* dfwPropertyPerm;
	dfwPropertyPerm = new DataFieldSubWrapperT<PropertyHolder, const Property*, const Permission&,Permission>(&PropertyHolder::getProperty, &Property::getPermission, "Permission", 2);
	fields->add(dfwPropertyPerm);
	return fields;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::PropertiesProvider, "", 1.01, _static_PropertiesProvider)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

PropertiesProvider::PropertiesProvider( const Object* owner )
	: MapT<std::string, PropertyHolder*>() {
	this->listener = NULL;
	this->defaultPermission.setMask( Permission::Read, Permission::Read, Permission::Read );
	this->listPermission.setMask( Permission::RW, Permission::Read, Permission::Read );
	if ( owner == NULL ) return;
	this->listPermission = owner->getPermission();
 }

PropertiesProvider::PropertiesProvider( const PropertiesProvider& m )
	: MapT<std::string, PropertyHolder*>()  {
	this->listener = NULL;
	*this = m;
}

PropertiesProvider::~PropertiesProvider() {
	clear();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////// REENTRANT GETTERS

Property PropertiesProvider::get( const std::string& key ) const {
	MutexLockerRead mL(&this->mux);
	const ConstIterator it = this->map.find(key);
	if ( it == this->map.end() )
		return Property::EmptyProperty;

	it->second->transactionBeginRead();
	if ( it->second->data == NULL ) {
		it->second->transactionEnd();
		return Property::EmptyProperty;
	 }
	PROFILING_ENTER(it->second->data, 0)
	if ( it->second->monitorRead && this->listener != NULL ) {
		bool accepted = this->listener->readingProperty(it->second->data, this);
		if ( !accepted ) {
			PROFILING_EXIT(it->second->data, 0)
			it->second->transactionEnd();
			return Property::EmptyProperty;
		 }
	 }
	Property propDeepCopy = *it->second->data; // copy on assignment
	PROFILING_EXIT(it->second->data, 0)
	it->second->transactionEnd();
	return propDeepCopy;
 }

Property* PropertiesProvider::getCopyOf( const std::string& key ) const {
	const ConstIterator it = this->map.find(key);
	if ( it == this->map.end() || it->second == NULL )
		return NULL;
	it->second->transactionBeginRead();
	if ( it->second->data == NULL ) {
		it->second->transactionEnd();
		return NULL;
	 }
	PROFILING_ENTER(it->second->data, 0)
	if ( it->second->monitorRead && this->listener != NULL ) {
		bool accepted = this->listener->readingProperty(it->second->data, this);
		if ( !accepted ) {
			PROFILING_EXIT(it->second->data, 0)
			it->second->transactionEnd();
			return NULL;
		 }
	 }
	Property* propDeepCopyPtr = it->second->data->clone(); // virtual copy constr.
	PROFILING_EXIT(it->second->data, 0)
	it->second->transactionEnd();
	return propDeepCopyPtr;
 }

Variant PropertiesProvider::getValueOf( const std::string& key, const Variant& defaultValue ) const {
	MutexLockerRead mL(&this->mux);
	const ConstIterator it = this->map.find(key);
	if ( it == this->map.end() || it->second == NULL )
		return defaultValue;
	it->second->transactionBeginRead();
	if ( it->second->data == NULL ) {
		it->second->transactionEnd();
		return defaultValue;
	 }
	PROFILING_ENTER(it->second->data, 0)
	if ( it->second->monitorRead && this->listener != NULL ) {
		bool accepted = this->listener->readingProperty(it->second->data, this);
		if ( !accepted ) {
			it->second->transactionEnd();
			return defaultValue;
		 }
	 }
	Variant dataDeepCopy = it->second->data->getValue(); // copy on assignment
	PROFILING_EXIT(it->second->data, 0)
	it->second->transactionEnd();
	return dataDeepCopy; // copy on assignment
 }

Permission PropertiesProvider::getPermissionOf( const std::string& key ) const {
	MutexLockerRead mL(&this->mux);
	const ConstIterator it = this->map.find(key);
	if ( it == this->map.end() || it->second == NULL || it->second->data == NULL )
		return Permission::EmptyPermission;
	it->second->transactionBeginRead();
	if ( it->second->data == NULL ) {
		it->second->transactionEnd();
		return Permission::EmptyPermission;
	 }
	PROFILING_ENTER(it->second->data, 0)
	Permission permDeepCopy = it->second->data->getPermission(); // copy on assignment
	PROFILING_EXIT(it->second->data, 0)
	it->second->transactionEnd();
	return permDeepCopy; // copy on assignment
 }

/////////////////////////////////////////////////////////////////////////////////////////// REENTRANT GETTERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////// REENTRANT SETTERS

WRESULT PropertiesProvider::set( const Property& prop ) {
	// std::map doesn't invalidate current iterators/object when inserting
	// .. we lock-for-write only on REMOVAL
	// updating (=overwriting) will lock-for-write the target Property(Holder)
	MutexLockerRead mL(&this->mux);
	Iterator lb = this->map.lower_bound( prop.getKey() );
	if( lb != this->map.end() && !(this->map.key_comp()( prop.getKey(), lb->first)) ) {
		if ( lb->second == NULL ) { // should really never happen, its not really thread-safe
			lb->second = new PropertyHolder();
			lb->second->reset();
		 }
		lb->second->transactionBeginWrite();
		if ( lb->second->data == NULL ) { // should NEVER happen
			Property* propDeepCopy = prop.clone();
			if ( this->listener != NULL ) {
				bool accepted = this->listener->newProperty(propDeepCopy, this ); // new property
				if ( !accepted ) {
					delete propDeepCopy;
					lb->second->transactionEnd();
					return WRET_ERR_ILLEGAL_USE;
				 }
			 }
			lb->second->data = propDeepCopy;
			PROFILING_INIT(lb->second->data, this->listener)
			PROFILING_ENTER(lb->second->data, 1)
			PROFILING_EXIT(lb->second->data, 1)
			lb->second->transactionEnd();
			return WRET_OK;
		 }
		bool do_update_value = true;
		if ( lb->second->monitorWrite ) {
			PROFILING_ENTER(lb->second->data, 1)
			// updating property, listener is allowed to: edit old property, avoid overwrite, read any other property
			bool accepted = this->listener->updatingProperty(do_update_value, prop.getValue(), *lb->second->data, this);
			if ( !accepted ) {
				PROFILING_EXIT(lb->second->data, 1)
				lb->second->transactionEnd();
				return WRET_ERR_ILLEGAL_USE;
			 }
		 }
		if ( do_update_value ) {
			*lb->second->data = prop; // copy on assignment, overwrite all fields
			if ( prop.getPermission().isEmpty() ) // empty permissions are NOT valid
				lb->second->data->setPermission( this->defaultPermission );
		 }
		else if ( !prop.getPermission().isEmpty() ) // empty permissions are NOT valid
			lb->second->data->setPermission( prop.getPermission() );
		PROFILING_EXIT(lb->second->data, 1)
		lb->second->transactionEnd();
	 }
	else {
		Property* propDeepCopy = prop.clone();
		if ( this->listener != NULL ) {
			bool accepted = this->listener->newProperty(propDeepCopy, this ); // new property
			if ( !accepted ) {
				delete propDeepCopy; // wasted memory, but shouldn't happen often!
				return WRET_ERR_ILLEGAL_USE;
			 }
		 }
		PropertyHolder* holder = new PropertyHolder();
		holder->reset();
		holder->data = propDeepCopy;
		PROFILING_INIT(holder->data, this->listener)
		PROFILING_ENTER(holder->data, 1)
		PROFILING_EXIT(holder->data, 1)
		// Use lb as a hint to insert, so it can avoid another lookup
		this->map.insert(lb, std::pair<std::string,PropertyHolder*>(prop.getKey(), holder));
	 }
	return WRET_OK;
 }

WRESULT PropertiesProvider::set( const std::string& key, const Variant& value ) {
	MutexLockerRead mL(&this->mux);
	return _set_( key, value, NULL, 0, true );
 }

WRESULT PropertiesProvider::set( const std::string& key, const Variant& value, const Permission& permission, short permission_overwrite_mode ) {
	//MutexLockerRead mL(&this->mux);
	return _set_( key, value, &permission, permission_overwrite_mode, false );
 }

WRESULT PropertiesProvider::remove( const std::string& key ) {
	MutexLockerWrite mL(&this->mux);
	Iterator it = this->map.find(key);
	if ( it == this->map.end() )
		return WRET_ERR_PARAM;
	if ( it->second == NULL ) {
		this->map.erase(it);
		return WRET_ERR_PARAM;
	 }
	if ( it->second->data == NULL ) {
		delete it->second;
		this->map.erase(it);
		return WRET_ERR_PARAM;
	 }
	if ( this->listener != NULL ) {
		bool accepted = this->listener->removingProperty(it->second->data, this ); // removing property
		if ( !accepted ) {
			return WRET_ERR_ILLEGAL_USE;
		 }
	 }
	delete it->second->data;
	delete it->second;
	this->map.erase(it);
	return WRET_OK;
 }

WRESULT PropertiesProvider::monitor( const std::string& key, bool write, bool read, bool removal ) {
	MutexLockerRead mL(&this->mux);
	Iterator it = this->map.find(key);
	if ( it == this->map.end() || it->second == NULL )
		return WRET_ERR_PARAM;
	it->second->transactionBeginWrite();
	it->second->monitorWrite = write;
	it->second->monitorRead = read;
	it->second->monitorExecute = removal;
	it->second->transactionEnd();
	return WRET_OK;
 }

/////////////////////////////////////////////////////////////////////////////////////////// REENTRANT SETTERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////// NON-LOCKING SETTERS

WRESULT PropertiesProvider::set( Property* prop ) {
	if ( prop == NULL ) return WRET_ERR_PARAM;
	Iterator lb = this->map.lower_bound( prop->getKey() );
	if( lb != this->map.end() && !(this->map.key_comp()( prop->getKey(), lb->first)) ) {
		if ( lb->second == NULL ) { // should really never happen, its not really thread-safe
			lb->second = new PropertyHolder();
			lb->second->reset();
		 }
		lb->second->transactionBeginWrite();
		if ( lb->second->data == NULL ) // should never happen
			lb->second->data = prop;
		else
			delete lb->second->data; // bad performance, but hopefully doesn't happen often
		lb->second->data = prop; // acquire pointer
		PROFILING_INIT(lb->second->data, this->listener)
		PROFILING_ENTER(lb->second->data, 1)
		PROFILING_EXIT(lb->second->data, 1)
	 }
	else {
		PropertyHolder* nHolder = new PropertyHolder();
		nHolder->reset();
		nHolder->data = prop;
		PROFILING_INIT(nHolder->data, this->listener)
		PROFILING_ENTER(nHolder->data, 1)
		// Use lb as a hint to insert, so it can avoid another lookup
		this->map.insert(lb, std::pair<std::string,PropertyHolder*>(prop->getKey(), nHolder));
		PROFILING_EXIT(nHolder->data, 1)
	 }
	return WRET_OK;
 }

WRESULT PropertiesProvider::update( const std::string& key, const Variant& value, bool callback ) {
	return _set_( key, value, NULL, 0, callback );
 }

WRESULT PropertiesProvider::_set_( const std::string& key, const Variant& value, const Permission* permission, short permission_overwrite_mode, bool callback ) {
	// list mutex is locked by caller
	// std::map doesn't invalidate current iterators/object when inserting
	// .. we lock-for-write only on REMOVAL
	// updating (=overwriting) will lock-for-write the target Property(Holder)
	Iterator lb = this->map.lower_bound( key );
	if( lb != this->map.end() && !(this->map.key_comp()( key, lb->first)) ) {
		if ( lb->second == NULL ) { // should really never happen, its not really thread-safe
			lb->second = new PropertyHolder();
			lb->second->reset();
		 }
		lb->second->transactionBeginWrite();
		if ( lb->second->data == NULL ) { // should NEVER happen
			Property* propNew = new Property(key, value);
			if ( permission != NULL )
				propNew->setPermission(*permission);
			else
				propNew->setPermission(this->defaultPermission);
			if ( callback && this->listener != NULL ) {
				bool accepted = this->listener->newProperty(propNew, this ); // new property
				if ( !accepted ) {
					delete propNew;
					return WRET_ERR_ILLEGAL_USE;
				 }
			 }
			lb->second->data = propNew;
			PROFILING_INIT(lb->second->data, this->listener)
			PROFILING_ENTER(lb->second->data, 1)
			PROFILING_EXIT(lb->second->data, 1)
			lb->second->transactionEnd();
			return WRET_OK;
		 }
		WRESULT ret = updating( lb->second, value, permission, permission_overwrite_mode, callback );
		lb->second->transactionEnd();
		return ret;
	 }
	else {
		Property* propNew = new Property(key, value);
		if ( permission != NULL )
			propNew->setPermission(*permission);	/////////////////////////////////////////////////////////////////BUG, todo inherit permission_overwrite_mode
		else
			propNew->setPermission(this->defaultPermission);
		if ( callback && this->listener != NULL ) {
			bool accepted = this->listener->newProperty(propNew, this ); // new property
			if ( !accepted ) {
				delete propNew; // wasted memory, but shouldn't happen often!
				return WRET_ERR_ILLEGAL_USE;
			 }
		 }
		PropertyHolder* holder = new PropertyHolder();
		holder->reset();
		holder->data = propNew;
		PROFILING_INIT(holder->data, this->listener)
		PROFILING_ENTER(holder->data, 1)
		PROFILING_EXIT(holder->data, 1)
		// Use lb as a hint to insert, so it can avoid another lookup
		this->map.insert(lb, std::pair<std::string,PropertyHolder*>(key, holder));
	 }
	return WRET_OK;
 }


WRESULT PropertiesProvider::updating( PropertyHolder* prop, const Variant& value, const Permission* permission, short permission_overwrite_mode, bool callback ) {
	if ( prop == NULL || prop->data == NULL ) return WRET_ERR_PARAM;
	bool do_update_value = true;
	PROFILING_ENTER(prop->data, 1)
	if ( callback && prop->monitorWrite && this->listener != NULL ) {
		// updating property, listener is allowed to: edit old property, avoid overwrite, read any other property
		bool accepted = this->listener->updatingProperty(do_update_value, value, *prop->data, this);
		if ( !accepted ) {
			PROFILING_EXIT(prop->data, 1)
			return WRET_ERR_ILLEGAL_USE;
		 }
	 }
	if ( do_update_value ) {
		prop->data->setValue(value); // overwrite field
		if ( permission_overwrite_mode != Permission::Overwrite_None ) {
			if ( permission != NULL )
				prop->data->setPermission(*permission);	/////////////////////////////////////////////////////////////////BUG, todo inherit permission_overwrite_mode
			else if ( prop->data->getPermission().isEmpty() ) // empty permission is NOT valid
				prop->data->setPermission(this->defaultPermission);
		 }
	 }
	PROFILING_EXIT(prop->data, 1)
	return WRET_OK;
 }

bool PropertiesProvider::reading( const PropertyHolder* prop ) const {
	if ( prop == NULL || prop->data == NULL ) return false;
	if ( !prop->monitorRead || this->listener == NULL ) return true;
	return this->listener->readingProperty(prop->data, this);
 }

///////////////////////////////////////////////////////////////////////////////////////// NON-LOCKING SETTERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////



void PropertiesProvider::setPermission( const Permission& value, short overwrite_mode, bool when_empty, bool nested ) {
	this->listPermission.inherit( value, overwrite_mode, when_empty );
	this->defaultPermission.inherit( value, overwrite_mode, when_empty );
	if ( !nested )
		return;
	this->mux.lockForWrite();
	Iterator it;
	const ConstIterator it_end = this->map.end();
	for( it=this->map.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( it->second->data == NULL ) continue;
		it->second->data->setPermission( this->listPermission, overwrite_mode, when_empty );
	 }
	this->mux.unLock();
 }


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////// OPERATORS

PropertiesProvider& PropertiesProvider::operator=( const PropertiesProvider& m ) {
	if (this == &m) return *this; // same object?
	m.mux.lockForRead();
	this->mux.lockForWrite();
	this->listPermission = m.listPermission;
	this->defaultPermission = m.defaultPermission;
	free();
	Iterator it;
	const ConstIterator it_end = m.map.end();
	for( it=m.map.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( it->second->data == NULL ) continue;
		PropertyHolder* pHolder = new PropertyHolder();
		pHolder->data = it->second->data->clone();
		pHolder->monitorExecute = it->second->monitorExecute;
		pHolder->monitorRead = it->second->monitorRead;
		pHolder->monitorWrite = it->second->monitorWrite;
		this->map.insert(std::pair<std::string,PropertyHolder*>( it->first, pHolder ));
	 }

	this->mux.unLock();
	m.mux.unLock();
	return *this;
 }

bool PropertiesProvider::operator==(const PropertiesProvider &other) const {
	other.mux.lockForRead();
	this->mux.lockForRead();
	if ( this->map.size() != other.map.size() ) {
		this->mux.unLock();
		other.mux.unLock();
		return false;
	 }
	const PropertyHolder* pr = NULL;
	Iterator it;
	const ConstIterator it_end = other.map.end();
	for( it=other.map.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		pr = find(it->first);
// BUG
	 }
	this->mux.unLock();
	other.mux.unLock();
	return true;
 }



/////////////////////////////////////////////////////////////////////////////////////////////////// OPERATORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// SERIALIZATION

List* PropertiesProvider::toList( short index ) const {
	List* list = new List();
	transactionBeginRead();
	ConstIterator it;
	list->reserve( this->map.size() );
	const ConstIterator it_end = this->map.end();
	for( it=this->map.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		it->second->transactionBeginRead();
		if ( it->second->data == NULL ) {
			continue;
			it->second->transactionEnd();
		 }
		switch(index) {
			case 2:		list->push_back<Variant>( it->second->data->getPermission().clone() ); break;
			case 1:		list->push_back<Variant>( it->second->data->getValue().clone() ); break;
			case 0:
			default:	list->push_back<Variant>( it->second->data->getKey() ); break;
		 }
		it->second->transactionEnd();
	 }
	transactionEnd();
	return list;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Table* PropertiesProvider::toTable( bool include_permission ) const {
	transactionBeginRead();
	Table* table = new Table( this->map.size(), 2+(short)include_permission, true );
	table->setTableName("Properties");
	table->setHeaderHorizontal("Name", 0);
	table->setHeaderHorizontal("Value", 1);
	if ( include_permission )
		table->setHeaderHorizontal("Permission", 2);
	unsigned int iRow = 0;
	ConstIterator it;
	const ConstIterator it_end = this->map.end();
	for( it=this->map.begin(); it!=it_end; ++it, ++iRow ) {
		if ( it->second == NULL ) continue;
		it->second->transactionBeginRead();
		if ( it->second->data == NULL ) {
			continue;
			it->second->transactionEnd();
		 }
		table->set<Variant>( it->second->data->getKey(), iRow, 0 );
		table->set( it->second->data->getValue().clone(), iRow, 1 );
		if ( include_permission )
			table->set( it->second->data->getPermission().clone(), iRow, 2 );
		it->second->transactionEnd();
	 }
	transactionEnd();
	return table;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void PropertiesProvider::setPropertiesProviderListener( IPropertiesProviderListener* pp_listener ) {
	transactionBeginWrite();
	this->listener = pp_listener;
	if ( this->listener != NULL ) {
		// force a reinitialization of properties' profiles, costly but tipically called only once
		ConstIterator it;
		const ConstIterator it_end = this->map.end();
		for( it=this->map.begin(); it!=it_end; ++it ) {
			if ( it->second == NULL ) continue;
			it->second->transactionBeginRead();
			if ( it->second->data == NULL ) {
				continue;
				it->second->transactionEnd();
			 }
			PROFILING_INIT(it->second->data, this->listener)
			it->second->transactionEnd();
		 }
	 }
	transactionEnd();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string PropertiesProvider::toString() const {
	return Utilities::format("%s[%d]", getClassNameOnlyOf(getClassName()), count());
 }

bool PropertiesProvider::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = this->listPermission.updateHash( hash_context );
	ConstIterator it;
	const ConstIterator it_end = this->map.end();
	for( it=this->map.begin(); it!=it_end; ++it ) {
		ret = hash_context->update( it->first ) & ret;
		if ( it->second == NULL ) continue;
		if ( it->second->data == NULL ) continue;
		ret = it->second->data->updateHash(hash_context) & ret;
	 }
	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////////// SERIALIZATION
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

PropertiesProvider& PropertiesProvider::intersect( PropertiesProvider& other_props, bool overwrite_value, bool overwrite_perms, bool callbacks ) {
	other_props.mux.lockForRead();
	this->mux.lockForWrite();
	Iterator it;
	const ConstIterator it_end = other_props.map.end();
	for( it=other_props.map.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( it->second->data == NULL ) continue;
		PropertyHolder* prop = find( it->first );
		if ( prop == NULL || prop->data == NULL ) {
			_set_( it->first, it->second->data->getValue(), &it->second->data->getPermission(), Permission::Overwrite_All, callbacks );
		 }
		else { // overwrite?
			Variant newValue = prop->data->getValue();
			if ( overwrite_value )
				newValue = it->second->data->getValue();
			if ( overwrite_perms )
				_set_( it->first, newValue, &it->second->data->getPermission(), Permission::Overwrite_All, callbacks );
			else
				_set_( it->first, newValue, &it->second->data->getPermission(), Permission::Overwrite_None, callbacks );
		 }
	 }
	this->mux.unLock();
	other_props.mux.unLock();
	return *this;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT PropertiesProvider::updateObjectKeys( const Object* object, short permission_overwrite_mode ) {
	if ( object == NULL ) return WRET_ERR_PARAM;
	WRESULT ret = WRET_OK;
	Permission cperm_readonly = object->getPermission();
	cperm_readonly.setMask( Permission::Read, Permission::Read, Permission::Read );
	ret += set( _KEY_entity, object->getEntityID(), cperm_readonly, permission_overwrite_mode );
	ret += set( _KEY_name, object->getName(), object->getPermission(), permission_overwrite_mode );
	ret += set( _KEY_type, string(object->getClassName()), cperm_readonly, permission_overwrite_mode );
	ret += set( _KEY_owner, object->getPermission().getOwner(), object->getPermission(), permission_overwrite_mode );
	ret += set( _KEY_group, object->getPermission().getGroup(), object->getPermission(), permission_overwrite_mode );
	ret += set( _KEY_rights, object->getPermission().getMask(), object->getPermission(), permission_overwrite_mode );
	if ( object->getParentObject() == NULL )
		ret += set( _KEY_parent, "", cperm_readonly, permission_overwrite_mode );
	else
		ret += set( _KEY_parent, object->getParentObject()->getEntityID(), cperm_readonly, permission_overwrite_mode );

	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
